<?php
namespace Library;

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\HttpFoundation\Response;
use Knp\Component\Pager\Paginator;

class BaseController extends Controller
{
    /**
     * Generate exception or exception message
     *
     * @param type $messageKey
     * @param type $ex
     * @param type $showError
     * @return type
     */
    public function handleException($message, $ex = null, $returnMessage = false)
    {
        $message = $this->getMessage($message, $ex);
        if ($returnMessage) {
            return $message;
        } else {
            return $this->createNotFoundException($message);
        }
    }

    /**
     * Just get the exception message
     *
     * @param type $messageKey
     * @param type $ex
     * @return type
     */
    public function getExceptionMessage($messageKey, $ex)
    {
        return $this->handleException($messageKey, $ex, true);
    }
    
    /**
     * 
     * @param string $message
     */
    public function successMessage($message)
    {
        $this->setFlashBag('success', $message);
    }
    
    /**
     * 
     * @param string $message
     */
    public function infoMessage($message)
    {
        $this->setFlashBag('info', $message);
    }

    /**
     * 
     * @param type $message
     * @param type $ex
     */
    public function errorMessage($message, $ex = null)
    {
        $this->setFlashBag('error', $message, $ex);
    }

    /**
     * 
     * @param type $message
     * @param type $ex
     */
    public function warningMessage($message, $ex = null)
    {
        $this->setFlashBag('notice', $message, $ex);
    }
    
    /**
     * @return null|Session
     */
    public function getSession()
    {
        return $this->getRequest()->getSession();
    }
    
    /**
     * Set flash bag 
     *
     * @param type $messageKey
     * @param type $ex
     * @param type $showError
     * @return type
     */
    public function setFlashBag($messageType, $message, $ex = null)
    {
        $message = $this->getMessage($message, $ex);
        $this->getSession()->getFlashBag()->clear();
        $this->getSession()->getFlashBag()->add($messageType, $message);
    }
     
    /**
     * Return HTML message
     * 
     * @param type $alertType
     * @param Test $message
     * @return type
     */
    public function renderMessage($alertType, $message, $ex = null)
    {
        $message = $this->getMessage($message, $ex);
        return $this->render('::_message.html.twig', array(
            'alertType' => $alertType, 
            'alert' => $message));
    }


    /**
     * Returns Symfony translator, which is used for i18n
     *
     * @return Translator
     */
    public function getTranslator()
    {
        return $this->get('translator');
    }

    /**
     * Returns Symfony validator service
     *
     * @return $validator Validator
     */
    public function getValidator()
    {
        return $this->get('validator');
    }

    /**
     * @return \Symfony\Component\Security\Core\SecurityContext
     */
    public function getSecurityContext()
    {
        /** @var SecurityContext $securityContext  */
        $securityContext = $this->get('security.context');

        return $securityContext;
    }
       
    /**
     * 
     * @param type $year
     * @param type $month
     * @param type $day
     * @return \DateTime
     */
    public function getDate($year = null , $month = null, $day = null)
    {
        $date = new \DateTime('now');
        if ($year && $month && $day) {
            $date->setDate($year, $month, $day);
        }
        return $date;
    }
    
    /**
     * 
     * @param type $year
     * @param type $month
     * @param type $day
     * @return type
     */
    public function getTimestamp($year = null , $month = null, $day = null)
    {
        $date = $this->getDate($year, $month, $day);
        return $date->getTimestamp();
    }
   

    /**
     * Get the current enviroment(.e.g. dev)
     *
     * @return type
     */
    public function getEnvironment()
    {
        return $this->get('kernel')->getEnvironment();
    }

    /**
     * 
     * @param type $result
     * @param array $options
     * @param type $limitPage
     * @return type
     */
    public function getPagination($result, array $options, $limitPage = 10)
    {
        $paginator  = $this->get('knp_paginator');
        $pagination = $paginator->paginate(
            $result,
            $this->getRequest()->query->get('page', 1),
            $limitPage
        );
        $pagination->setCustomParameters($options);
        return $pagination;
    }

   
    /**
     * 
     * @return type
     */
    public function getHttpReferer()
    {
        $request = $this->getRequest();
        return $request->headers->get('referer');
    }
    
    /**
     * 
     * @return type
     */
    public function getHostName()
    {
        $request = $this->getRequest();
        return $request->headers->get('host');
    }

    
    /**
     * Get parameter
     * 
     * @param type $parameter
     * @return type
     */
    public function getParameter($parameter)
    {
        return $this->container->getParameter($parameter);
    }    

    /**
     * Get parameters
     * 
     * @param Array $parameters
     * @return Array of parameters
     */
    public function getParameters($parameters)
    {
        $parametersArray = array();
        if (is_array($parameters)) {
            foreach ($parameters as $parameter) {
                $parametersArray[$parameter] = $this->getParameter($parameter);
            }
        }
        
        return $parametersArray;
    }
    
        
    /**
     * 
     * @param type $path
     * @return type
     */
    public function getYmlFile($path)
    {
        $yaml = new Parser();
        $value = $yaml->parse(file_get_contents($path));

        if (is_array($value) && array_key_exists('parameters', $value)) {
            $value = $value['parameters'];
        }

        return $value;
    }

    /**
     * Generate the base url
     *
     * @return mixed|string
     */
    public function getBaseUrl()
    {
        $baseUrl = $this->container->getParameter('elmopms_url');
        //remove the trailing slash
        $baseUrl = rtrim($baseUrl, '/');

        return $baseUrl;
    }

        
    /**
     * Get dispatcher service
     * 
     * @return type
     */
    public function getDispatcher()
    {
        return $this->get('event_dispatcher');
    }
    
    /**
     * Overwrite render to put title variable
     * @param type $view
     * @param array $parameters
     * @param \Symfony\Component\HttpFoundation\Response $response
     * @return type
     */
    public function render($view, array $parameters = array(), Response $response = null)
    {
//         if (!isset($parameters['elmo_pms_title'])) {
//             $companySystemName = $this->get('elmo_pms.company_configuration')->getValue('general', 'companySystemName');
//             if ( !empty($companySystemName) ) {
//                 $parameters['elmo_pms_title'] = $companySystemName;
//             }
//         }
        return parent::render($view, $parameters, $response);
    }
    
    /**
     * Soft-delete the given entity
     *
     * @param Object $entity        Entity to delete
     * @param EntityManager $em     Entity Manager
     * @param bool $deleteAssoc     Delete associated objects?
     * @throws \ErrorException
     * @throws \Exception
     */
    public function removeEntity($entity, EntityManager $em = null, $deleteAssoc = true)
    {
            // Soft-delete primary object
            /** @var BaseEntity $entity */
            $entity->setDeleted(true);
            $entity->setDeletedTime();
            // Soft-delete associated fields if requested
            if ($deleteAssoc === true) {
                /** @var ClassMetadata $metaData */
                $metaData = $this->getDoctrinManager()->getClassMetadata($entity->getClassName());
                $assMaps = $metaData->getAssociationMappings();
                if (!empty($assMaps)) {
                    foreach ($assMaps as $assEntity) {
                        $fieldName = $assEntity['fieldName'];
                        $getFunction = 'get'. ucfirst($fieldName);
                        if (method_exists($entity, $getFunction)) {
                            // get all association entities
                            $associations = $entity->$getFunction();
                        }
                        if (!empty($associations)) {
                            foreach ($associations as $association) {
                                /**
                                 *
                                 * Update: 28/10/2014 by Thang Nguyen
                                 * Should use $associations->removeElement($association) to delete
                                 * The many-to-many name may be different from entity Name
                                 * e.g in ScheduledReport, receivers is the name for many-to-many to user
                                 *
                                 */
                                
                                if ($association instanceof BaseEntity) {
                                    $reflect = new \ReflectionClass($association);
                                    $removeFunction = 'remove'. ucfirst($reflect->getShortName());
                                    if (method_exists($entity, $removeFunction)) {
                                        $entity->$removeFunction($association);
                                    } else {
                                        if (method_exists($association, "removeElement")) {
                                            $association->removeElement();
                                        }else{
                                            $this->removeEntity($association);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            // Apply to relevant EntityManager
            if ($em == null) {
                $this->getDoctrinManager()->persist($entity);
                $this->getDoctrinManager()->flush();
            } else {
                $em->persist($entity);
                $em->flush();
            }

    }
    
        
    /**
     * Generate a message 
     * 
     * @param string $message
     * @param type $ex
     * @return string
     */
    private function getMessage($message, $ex = null)
    {
        $exceptionError = $this->getExceptionError($ex);
        $message = $this->translate($message) . "." . $exceptionError;        
        return $message;
    }
        
    /**
     * Get exception error
     *
     * @param type $ex
     * @param type $forceToShow
     * @return type $exceptionError
     */
    private function getExceptionError($ex, $forceToShow = false)
    {
        $exceptionError = '';
        $showErrorConfig = $this->container->getParameter('show_exception_error_detail');
        if ($ex != null && is_a($ex, '\Exception')) {
            $exceptionError = ($showErrorConfig || $forceToShow)? ' ' . $ex->getMessage() : '';
        }
        return $exceptionError;
    }

    /**
     * @return \Doctrine\Common\Persistence\ObjectManager|object
     */
    private function getDoctrinManager()
    {
        return $this->getDoctrine()->getManager();
    }
    
    /**
     * Translate a message
     *
     * @param $message
     * @return string
     */
    public function translate($message, $messageParam = array())
    {
        if (is_array($message)) {
            $messageParam = array();
            if (array_key_exists(1, $message)) {
                $messageParam = $message[1];
            }
            if (array_key_exists(0, $message)) {
                $translatedMessage = $this->getTranslator()->trans($message[0], $messageParam);
            } else {
                $translatedMessage = 'Null';
            }
        } else {
            $translatedMessage = $this->getTranslator()->trans(
                $message,
                $messageParam
            );
        }
        return $translatedMessage;
    }

}